codeforces 710F Trie树+kmp Trie树板子和kmp板子

You should process m queries over a set D of strings. Each query is one of three kinds:

Add a string s to the set D. It is guaranteed that the string s was not added before.
Delete a string s from the set D. It is guaranteed that the string s is in the set D.
For the given string s find the number of occurrences of the strings from the set D. If some string p from D has several occurrences in s you should count all of them.
Note that you should solve the problem in online mode. It means that you can’t read the whole input at once. You can read each query only after writing the answer for the last query of the third type. Use functions fflush in C++ and BufferedWriter.flush in Java languages after each writing in your program.

Input
The first line contains integer m (1 ≤ m ≤ 3·105) — the number of queries.

Each of the next m lines contains integer t (1 ≤ t ≤ 3) and nonempty string s — the kind of the query and the string to process. All strings consist of only lowercase English letters.

The sum of lengths of all strings in the input will not exceed 3·105.

Output
For each query of the third kind print the only integer c — the desired number of occurrences in the string s.

Example
Input
5
1 abc
3 abcabc
2 abc
1 aba
3 abababc
Output
2
2
Input
10
1 abc
1 bcd
1 abcd
3 abcd
2 abcd
3 abcd
2 bcd
3 abcd
2 abc
3 abcd
Output
3
2
1
0

题目大意:给M个操作 1.插入一个串S 2.删除一个串S 3.给出一个串S,询问之前存在(插入后未被删除的)的串在S中出现的次数。

因为ac自动机 不支持删除和修改操作 每次都要重新配置 失配指针会爆炸。

大佬们都是用 ac自动机各种合并做的 太强了。。弱弱表示只会板子,别的都不会

因为字符串总长度不超过3e5 那么就很可以做文章了
我们把低于1000的存进trie树里面 那么最多也就300个串 肯定不到,因为还有查询串的长度。
然后根据一个串的子串就是所有后缀的前缀,所以 遍历一下查询串每次查询一次 其实也就查询了
查询串长度*1000 肯定到不了。当时可以把小串的都查询遍了,然后对于大于1000的串,暴力kmp就好,因为trie树是支持删除操作的。

大于1000的串也是顶多不到100个,复杂度和上面差不多,另外加个标记是删除还是增加 统计进去就好,总的复杂也就1e7左右

#include <bits/stdc++.h>
using namespace std;
const int MAXN=3e5+100;
const int CASE_SIZE=26;

typedef long long ll;
char str[MAXN];
struct Trie
{
    int child[MAXN][CASE_SIZE],value[MAXN],trieN,root;
    void init()
    {
        trieN=root=0;value[root]=0;
        memset(child[root],-1,sizeof(child[root]));
    }
    int newnode()
    {
        trieN++;value[trieN]=0;
        memset(child[trieN],-1,sizeof(child[trieN]));
        return trieN;
    }
    void insert(char *s,int val)
    {
        int x=root;
        for(int i=0;s[i];i++)
        {
            int d=s[i]-'a';
            if(child[x][d]==-1)
                child[x][d]=newnode();
            x=child[x][d];
        }
        value[x]+=val;//这种是读完整串才能++
    }
    int search(char *s)
    {
        int sum=0,x=root;
        for(int i=0;s[i];i++)
        {
            int d=s[i]-'a';
            if(child[x][d]==-1)
                break;
            x=child[x][d];
            sum+=value[x];
        }
        return sum;
    }
}trie;



struct KMP{
    int Next[MAXN];
    void getNext(string s,int len){
        memset(Next,0,sizeof(Next));
        int i=0,j=-1;Next[0]=-1;
        while(i<len)
        {
            if(j==-1||s[i]==s[j]){
                ++i;++j;
                Next[i]=j;
            }
            else j=Next[j];
        }
    }
    int search(string s,char *str)
    {
        int sum=0,j=0,lens=s.length(),lenstr=strlen(str);
        getNext(s,s.length());
        for(int i=0;i<lenstr;i++)
        {
            while(j>0&&s[j]!=str[i])
            {
                j=Next[j];
            }
            if(s[j]==str[i])
                j++;
            if(j==lens)
            {
                sum++;j=Next[j];
            }
        }
        return sum;
    }
}kmp;


string cun[MAXN];
int flag[MAXN];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
    int cnt=0;
    memset(flag,0,sizeof(flag));
    trie.init();
    for(int i=1;i<=n;i++)
    {
            int d;
            scanf("%d %s",&d,str);
            int len=strlen(str);
            if(d==1)
            {
                if(len<=1000)
                trie.insert(str,1);
                else cun[++cnt]=string(str),flag[cnt]=1;
            }
            if(d==2)
            {
                if(len<=1000)
                trie.insert(str,-1);
                else cun[++cnt]=string(str),flag[cnt]=-1;
            }
            if(d==3)
            {
                ll ans=0;
                for(int i=0;i<len;i++)
                    ans+=trie.search(str+i);
                for(int i=1;i<=cnt;i++)
                {
                    if(len<cun[i].length()) continue;
                    ans+=kmp.search(cun[i],str)*flag[i];
                }
                printf("%I64d\n",ans );
                 fflush(stdout);
            }
    }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值